package com.sos.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.concurrent.TimeUnit;

/**
 * JWT工具类（修复密钥长度问题，支持HS512算法）
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtils {

    // 从配置文件读取Base64编码的密钥
    @Value("${jwt.secret}")
    private String base64Secret;

    // 令牌有效期（2小时）
    @Value("${jwt.expiration}")
    private long expiration;

    // 刷新令牌有效期（7天）
    @Value("${jwt.refresh-expiration}")
    private long refreshExpiration;

    // Redis模板（用于黑名单校验）
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 核心方法：解码Base64密钥，生成HS512专用SecretKey
     * 确保密钥长度≥512位，符合JWT规范
     */
    private SecretKey getSecretKey() {
        // 1. 解码Base64字符串为字节数组
        byte[] keyBytes = Base64.getDecoder().decode(base64Secret);
        // 2. 生成HS512算法专用密钥
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 从令牌中获取用户名（Subject）
     */
    public String getUsernameFromToken(String token) {
        // 如果token为空，直接返回null
        if (token == null || token.isEmpty()) {
            return null;
        }
        
        return getClaimFromToken(token, Claims::getSubject);
    }

    /**
     * 从令牌中获取过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        // 如果token为空，直接返回null
        if (token == null || token.isEmpty()) {
            return null;
        }
        
        return getClaimFromToken(token, Claims::getExpiration);
    }

    /**
     * 通用方法：从令牌中获取自定义声明
     */
    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        // 如果token为空，直接返回null
        if (token == null || token.isEmpty()) {
            return null;
        }
        
        final Claims claims = getAllClaimsFromToken(token);
        // 如果claims为null，直接返回null
        if (claims == null) {
            return null;
        }
        
        return claimsResolver.apply(claims);
    }

    /**
     * 解析令牌，获取所有声明（包含Header、Payload）
     */
    private Claims getAllClaimsFromToken(String token) {
        // 如果token为空，直接返回null
        if (token == null || token.isEmpty()) {
            return null;
        }
        
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(getSecretKey())  // 使用合规的HS512密钥
                    .setAllowedClockSkewSeconds(3600) // 允许1小时的时钟偏差，以更好地处理过期令牌
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            // 对于过期的令牌，仍然允许解析其声明内容
            return e.getClaims();
        } catch (MalformedJwtException e) {
            log.warn("JWT格式错误：{}", e.getMessage());
            return null;
        } catch (Exception e) {
            log.error("解析JWT时发生异常：", e);
            return null;
        }
    }

    /**
     * 检查令牌是否已过期
     */
    private Boolean isTokenExpired(String token) {
        // 如果token为空，视为已过期
        if (token == null || token.isEmpty()) {
            return true;
        }
        
        final Date expirationDate = getExpirationDateFromToken(token);
        // 如果无法获取过期时间，视为已过期
        if (expirationDate == null) {
            return true;
        }
        
        return expirationDate.before(new Date());
    }

    /**
     * 生成访问令牌（用于接口访问）
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return doGenerateToken(claims, username, expiration);
    }

    /**
     * 生成刷新令牌（用于获取新地访问令牌）
     */
    public String generateRefreshToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return doGenerateToken(claims, username, refreshExpiration);
    }

    /**
     * 生成令牌的核心逻辑
     */
    private String doGenerateToken(Map<String, Object> claims, String subject, long expirationTime) {
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                .setClaims(claims)  // 自定义声明（如用户角色等）
                .setSubject(subject)  // 用户名（唯一标识）
                .setIssuedAt(new Date(currentTime))  // 签发时间
                .setExpiration(new Date(currentTime + expirationTime))  // 过期时间
                .signWith(getSecretKey(), SignatureAlgorithm.HS512)  // 显式指定HS512算法
                .compact();  // 生成最终令牌
    }

    /**
     * 验证令牌有效性（用户名匹配 + 未过期 + 不在黑名单）
     */
    public Boolean validateToken(String token, String username) {
        // 如果token或username为空，视为无效
        if (token == null || token.isEmpty() || username == null || username.isEmpty()) {
            return false;
        }
        
        final String tokenUsername = getUsernameFromToken(token);
        return tokenUsername != null 
                && tokenUsername.equals(username)
                && !isTokenExpired(token)
                && !isTokenInBlacklist(token);
    }

    /**
     * 检查令牌是否在黑名单（退出登录时添加）
     */
    private boolean isTokenInBlacklist(String token) {
        // 如果token为空，视为在黑名单中
        if (token == null || token.isEmpty()) {
            return true;
        }
        
        String blacklistKey = "sos:blacklist:" + token;
        return Boolean.TRUE.equals(redisTemplate.hasKey(blacklistKey));
    }

    /**
     * 对外提供的令牌有效性检查（用于拦截器/过滤器）
     */
    public boolean isTokenValid(String token) {
        // 如果token为空，视为无效
        if (token == null || token.isEmpty()) {
            return false;
        }
        
        try {
            log.info("开始验证Token: {}", token);
            // 1. 验证令牌格式和签名
            Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token);
            // 2. 验证是否过期 + 是否在黑名单
            boolean isExpired = isTokenExpired(token);
            boolean isInBlacklist = isTokenInBlacklist(token);
            
            log.info("Token验证详情 - 是否过期: {}, 是否在黑名单: {}", isExpired, isInBlacklist);
            
            return !isExpired && !isInBlacklist;
        } catch (SignatureException ex) {
            log.error("JWT签名无效：{}", ex.getMessage());
        } catch (MalformedJwtException ex) {
            log.error("JWT格式错误：{}", ex.getMessage());
        } catch (ExpiredJwtException ex) {
            log.error("JWT已过期：{}", ex.getMessage());
        } catch (UnsupportedJwtException ex) {
            log.error("不支持的JWT类型：{}", ex.getMessage());
        } catch (IllegalArgumentException ex) {
            log.error("JWT声明为空：{}", ex.getMessage());
        } catch (Exception ex) {
            log.error("JWT验证过程中发生未知异常：", ex);
        }
        return false;
    }
}